package main

import (
	"encoding/json"
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
)

const (
	templatesDir = "private"
	resourcesDir = "public"
)

var index *template.Template

func init() {
	// load template files
	index = template.Must(template.ParseFiles(
		filepath.Join(templatesDir, "base.html"),
		filepath.Join(templatesDir, "viewing.html"),
		filepath.Join(templatesDir, "editing.html"),
	))

	// add functions to template index
	index.Funcs(map[string]interface{}{
		"getLabel": func(uid UID) *Label {
			return lexicon.Labels[uid]
		},
	})
}

func launchServer(addr string, readOnly bool) error {
	var mux = http.NewServeMux()

	mux.Handle("/", http.FileServer(http.Dir(resourcesDir)))

	// handlers that respond with HTML (for noscript version of front-end)
	mux.HandleFunc("/view.html", serveViewHTML)
	mux.HandleFunc("/exit.html", serveExitHTML)

	// handlers that respond with JSON (for dynamic front-end)
	mux.HandleFunc("/updates.json", serveUpdatesJSON)

	mux.HandleFunc("/exit", serveExitHTML)
	mux.HandleFunc("/info", serveInfo)
	mux.HandleFunc("/get", serveGet)
	mux.HandleFunc("/updates", serveUpdates)

	if !readOnly {
		mux.HandleFunc("/new", serveNew)
		mux.HandleFunc("/mod", serveMod)
		mux.HandleFunc("/del", serveDel)
	}

	return http.ListenAndServe(addr, mux)
}

/* ********************************** */

func serveTemplate(w http.ResponseWriter, name string, data interface{}) {
	if err := index.ExecuteTemplate(w, name, data); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func serveViewHTML(w http.ResponseWriter, r *http.Request) {
	switch r.FormValue("view") {
	case "Index":
		// TODO
	case "Node":
		uid := r.FormValue("uid")
		n, ok := lexicon.Nodes[uid]
		if ok {
			serveTemplate(w, "viewNode", n)
		} else {
			http.Error(w, fmt.Errorf("No node found for UID %s", uid), http.StatusNotFound)
		}
	}
}

func serveExitHTML(w http.ResponseWriter, r *http.Request) {
	save := r.FormValue("save") != "false"
	serveTemplate(w, "Exit", save)
	exit(save)
}

/* ********************************** */

func serveJSON(w http.ResponseWriter, e interface{}) {
	json.NewEncoder(w).Encode(e)
}

func serveArray(w http.ResponseWriter, items ...interface{}) {
	serveJSON(w, items)
}

func serveUpdatesJSON(w http.ResponseWriter, r *http.Request) {
	uid := r.FormValue("uid")
	modified := r.FormValue("modified")

	// check whether item on server has been removed or more recently modified.
	// send either {"uid":uid, "current":true},
	// {"uid":uid, "remove":true}, or updated item (with "modified" entry).

}

/* ********************************** */

func serveInfo(w http.ResponseWriter, r *http.Request) {
	// TODO
}

func serveGet(w http.ResponseWriter, r *http.Request) {
	r.ParseMultipartForm(1 << 20) // 1MB
	reqs := r.Form["get"]         // an array of UIDs or slugs in proper repr

	items := make([]interface{}, 0, len(reqs))

	if len(reqs) == 0 {
		// Serve all nodes
		for _, e := range lexicon.Nodes {
			items = append(items, e)
		}
	}

	for _, ruid := range reqs {
		if uid, err := ParseUID(ruid); err == nil {
			item, err := lexicon.RetrUID(uid)
			if err != nil {
				fmt.Println(err)
				continue
			}
			items = append(items, item)
		} else if slug, err := ParseSLUG(ruid); err == nil {
			item, err := lexicon.RetrSlug(slug)
			if err != nil {
				fmt.Println(err)
				continue
			}
			items = append(items, item)
		} else {
			fmt.Printf("Invalid request: Get %q\n", ruid)
		}
	}

	serveJSON(w, items)
}

func serveUpdates(w http.ResponseWriter, r *http.Request) {
	// TODO Check modifiedTime
}

func serveNew(w http.ResponseWriter, r *http.Request) {
	reqType, reqParent := r.FormValue("new"), r.FormValue("parent")

	var nParent *Node
	if uidParent, err := ParseUID(reqParent); err == nil {
		nParent = lexicon.Nodes[uidParent]
	}

	switch reqType {

	case "node": // New node
		if nParent != nil {
			// New child under parent node
			nNew := nParent.NewChild()
			serveArray(w, nParent, nNew)
		} else {
			// New root-level node
			nNew := lexicon.NewNode()
			serveArray(w, nNew)
		}

	case "field": // New field
		if node == nil {
			fmt.Printf("No such node: %q\n", reqNode)
			return
		}
		// TODO

	case "relation": // New relation
		if node == nil {
			fmt.Printf("No such node: %q\n", reqNode)
			return
		}
		// TODO

	}
}

func serveMod(w http.ResponseWriter, r *http.Request) {
}

func serveDel(w http.ResponseWriter, r *http.Request) {
}
